﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Practices.Unity;
using MiniStock.BusinessLogic.EventProcessor.EntityCache;
using MiniStock.BusinessLogic.EventProcessor.EventRepository;
using MiniStock.BusinessLogic.EventQueue.Queue;
using MiniStock.Entities;
using MiniStock.Entities.Common;
using MiniStock.Entities.Common.Events;
using MiniStock.Infrastructure;
using System.Diagnostics;
using MiniStock.Infrastructure.Configurator;
using MiniStock.Infrastructure.Queue;

namespace MiniStock.BusinessLogic.EventProcessor
{
    public class ProcessorEventQueueWorker : EventQueueWorkerBase
    {
        protected override QueuedEventWorkerBase GetWorkerForMessage(EntityEventBase message)
        {
            if (Workers == null)
                throw new ApplicationException("Processor must be initialized first");

            // Implement entity hash affinity
            var result =
                Workers.FirstOrDefault(i => i.GetEntityIdList().Contains(message.GetEntityHash()));

            // Fallback to default behavior
            if(result == null)
            {
                result = base.GetWorkerForMessage(message);
            }

            return result;
        }

        protected override QueuedEventWorkerBase CreateWorker(int index)
        {
            var configurator = RootContainer.Resolve<IEventProcessorConfigurator>();
            var result = RootContainer.Resolve<EventProcessorWorker>(new ResolverOverride[]
                                                                        {
                                                                            new PropertyOverride("OutputEventQueue", this.OutputEventQueue),
                                                                        });
            result.Name = this.Name + "." + index;
            result.InputEventQueue.Configure(RootContainer.Resolve<IEventQueueConfigurator>("InternalConfigurator"), result.Name + ".in");
            return result;
        }

        public void Configure(string workerName = null)
        {
            var configurator = RootContainer.Resolve<IEventProcessorConfigurator>();
            var pluginConfigurator = RootContainer.Resolve<IPluginConfigurator>();
            string name = workerName ?? configurator.GetName();
            var queueConfigurator = RootContainer.Resolve<IEventQueueConfigurator>();
            InputEventQueue.Configure(queueConfigurator, configurator.GetInputQueueRef(name));
            OutputEventQueue.Configure(queueConfigurator, configurator.GetOutputQueueRef(name));
            this.Name = name;

            var plugins = configurator.GetPluginRefs(name);
            foreach (var initializer in plugins
                .Select(i => pluginConfigurator.GetEntryPoint(i))
                .Select(i => Type.GetType(i))
                .Where(s => s != null)
                .Select(i => RootContainer.Resolve(i) as IProcessorPluginEntryPoint)
                .Where(s => s != null)
                )
            {
                SerializationHelper.RegisterKnownEventTypes(initializer.EventRootTypes);
                SerializationHelper.RegisterKnownEntityTypes(initializer.EntityRootTypes);
            }

            EntityBaseExtender.InitSerializer();
            EventBaseExtender.InitSerializer();

            InitThreadPool(configurator.GetThreadCount(name));
        }
    }
}
